package myLearnjdk.jdk.arithmetic;

import java.util.ArrayList;
import java.util.List;

/**
 * 数组动态扩容算法---netty源码
 * @author yangcheng  
 * @date 2019年8月18日  
 * @version V1.0
 */
public class IncriArray {
	
	/**
	 * 以512为分水岭，小于512的容量单位时，以16个容量单位为扩容大小---即扩容前后数组增加16个容量单位
	 */
    private static final int[] SIZE_TABLE;

    static {
        List<Integer> sizeTable = new ArrayList<Integer>();
        for (int i = 16; i < 512; i += 16) {
            sizeTable.add(i);
        }

        for (int i = 512; i > 0; i <<= 1) {
            sizeTable.add(i);
        }

        SIZE_TABLE = new int[sizeTable.size()];
        for (int i = 0; i < SIZE_TABLE.length; i ++) {
            SIZE_TABLE[i] = sizeTable.get(i);
        }
        System.out.println(sizeTable.toString());
    }

    /**
     * 根据传入的所需容量单位获取数组的初始化size
     * @param size
     * @return
     */
    private static int getSizeTableIndex(final int size) {
        for (int low = 0, high = SIZE_TABLE.length - 1;;) {
            if (high < low) {
                return low;
            }
            if (high == low) {
                return high;
            }

            int mid = low + high >>> 1;
            int a = SIZE_TABLE[mid];
            int b = SIZE_TABLE[mid + 1];
            if (size > b) {
                low = mid + 1;
            } else if (size < a) {
                high = mid - 1;
            } else if (size == a) {
                return mid;
            } else {
                return mid + 1;
            }
        }
    }
    
    /**
     * 根据传入的最大所需容量大小，返回容量数组的下标值，通过该值
     * 获取初始化数组的容量，目的是为了尽量减少数组的扩容次数
     * 使用二分法查找
     * @param size
     * @return
     */
    public static int getSuitableIndex(int index){
    	
    	
    	int minIndex = getSizeTableIndex(index);
        if (SIZE_TABLE[minIndex] < index) {
            minIndex = minIndex + 1;
        } else {
            minIndex = minIndex;
        }
        return minIndex;
    }
    
    private static final int INDEX_INCREMENT = 4; // 扩张步进
    
    private static final int INDEX_DECREMENT = 1;//收缩步进
    /**
     * 根据实际情况动态变更数据容量的方法
     * 
     * 如果实际容量大于上次预分配的数组容量，选取“当前索引+扩张步进”和最大索引中的min作为当前索引值
     * 通过当前索引值获取最新的容量
     * 
     * @param minimum  预期缓冲区大小的包含下限
     * @param maxmum  预期缓冲区大小的包含上限
     * @param initial 未收到反馈时的初始缓冲区大小
     * @param actualReadBytes  最近一次实际接收数据的容量大小
     */
    public static int  record(int minimum, int maxmum, int initial,int actualReadBytes) {
    	int nextReceiveBufferSize = 0;
    	boolean decreaseNow = true;
    	
    	int minIndex = getSizeTableIndex(minimum);
    	int maxIndex = getSizeTableIndex(maxmum);
    	int index = getSizeTableIndex(initial);
    	/**
    	 * 将当前索引安缩减步进缩减，并与实际读取字节数比较，如果实际读取数小于缩进后的数量
    	 */
        if (actualReadBytes <= SIZE_TABLE[Math.max(0, index - INDEX_DECREMENT - 1)]) {
            if (decreaseNow) {
            	/**
            	 * 取收缩后的索引与默认最小索引中的最大值，作为缩减后的新索引
            	 */
                index = Math.max(index - INDEX_DECREMENT, minIndex);
                nextReceiveBufferSize = SIZE_TABLE[index];
                decreaseNow = false;
            } else {
                decreaseNow = true;
            }
            
        } else if (actualReadBytes >= nextReceiveBufferSize) {
            /**
             * 如果实际容量大于上次预分配的数组容量，选取“当前索引+扩张步进”和最大索引中的min作为当前索引值
             * 通过当前索引值获取最新的容量
             */
        	index = Math.min(index + INDEX_INCREMENT, maxIndex);
            nextReceiveBufferSize = SIZE_TABLE[index];
            decreaseNow = false;
        }
        return nextReceiveBufferSize;
    }

	
	public static void main(String[] args) {
		int requirdNum = 1030;
		int suitIndex = getSuitableIndex(requirdNum);
		System.out.println("最优数组初始化大小为=="+SIZE_TABLE[suitIndex]);
		
		int newSize = record(512, 4096, 1024,requirdNum);
		System.out.println("根据实际情况变更之后=="+newSize);
	}

}
